open Myclasses;;

let map_bar = [|[|1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1|]; 
                [|1; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 1|];
                [|1; 0; 1; 1; 1; 1; 0; 1; 1; 0; 1; 1; 1; 0; 1|];
                [|1; 0; 1; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 1|];
                [|1; 0; 0; 0; 1; 0; 1; 0; 1; 0; 1; 0; 1; 0; 1|];
                [|1; 0; 1; 1; 0; 0; 1; 0; 0; 0; 1; 0; 1; 0; 1|];
                [|1; 0; 1; 1; 0; 1; 1; 1; 1; 1; 1; 0; 0; 0; 1|];
                [|1; 0; 0; 0; 0; 0; 0; 0; 0; 0; 1; 0; 1; 1; 1|];
                [|1; 0; 1; 1; 1; 1; 0; 1; 1; 0; 0; 0; 0; 0; 1|];
                [|1; 0; 0; 0; 0; 0; 0; 1; 1; 1; 1; 1; 1; 0; 1|];
                [|1; 1; 1; 1; 0; 1; 0; 1; 0; 0; 0; 0; 0; 0; 1|];
                [|1; 0; 0; 0; 0; 1; 0; 0; 0; 1; 1; 0; 1; 0; 1|];
                [|1; 0; 1; 1; 1; 0; 1; 0; 1; 0; 0; 0; 1; 0; 1|];
                [|1; 0; 0; 0; 0; 0; 0; 0; 0; 0; 1; 0; 0; 0; 1|];
                [|1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1|]|]
;;

let gmap = new gamemap;;

let map_width = float(15);;
let map_height = float(15);;
let pi = 3.14159265;;
let r = 0.49;;
let v = 0.05;;
let width = 1.;;
let colorb = (0.2, 0.2, 0.8);; 

let total_score = ref 0;;

let pacman = new player;;
let monster_g = new item;;
let gift_g = new item;;

let px = ref 7.5;;
let py = ref 7.5;;
let vx = ref 0.0;;
let vy = ref 0.0;;
let next_dir = ref 0;;
let now_dir = ref 0;;
let colorp = (1., 1., 0.);;
let counter = ref 0;;


let circle x px py r = [(sin x) *. r +. px, (cos x) *. r +. py];;

let check_hit_bar_x x y =
        if map_bar.(truncate (y /. width)).(truncate (x /. width)) = 1 || 
           map_bar.(truncate ((y +. r) /. width)).(truncate (x /. width)) = 1 ||
           map_bar.(truncate ((y -. r) /. width)).(truncate (x /. width)) = 1 then
            true
        else false
;;
let check_hit_bar_y x y =
        if map_bar.(truncate (y /. width)).(truncate (x /. width)) = 1 ||
           map_bar.(truncate (y /. width)).(truncate ((x +. r) /. width)) = 1 ||
           map_bar.(truncate (y /. width)).(truncate ((x -. r) /. width)) = 1 then
            true
        else false
;;

let check_item_change_dir item = 
    (*if (vx = 0.) && ((abs_float (y-.float(truncate(y)))) < 0.05) &&*)
    if (item#getNowDir = 0 || item#getNowDir = 2) && (item#getMoveCounter <= 0) && ((((item#getPoint)#getX-50) mod 100) < item#getSpeed) &&
       (map_bar.((item#getPoint)#getY / 100 + 1).((item#getPoint)#getX / 100) != 1 ||
        map_bar.((item#getPoint)#getY / 100 - 1).((item#getPoint)#getX / 100) != 1) then
            true
    else if (item#getNowDir = 1 || item#getNowDir = 3) && (item#getMoveCounter <= 0) && ((((item#getPoint)#getY-50) mod 100) < item#getSpeed) &&
       (map_bar.((item#getPoint)#getY / 100).((item#getPoint)#getX / 100 + 1) != 1 ||
        map_bar.((item#getPoint)#getY / 100).((item#getPoint)#getX / 100 - 1) != 1) then
            true
    else if ((item#getVelocity)#getX = 0) && ((item#getVelocity)#getY = 0) then
            true
    else false;
;;


let set_item_v vxx vyy item = 
    (item#getVelocity)#setX vxx;
    (item#getVelocity)#setY vyy;
    item#setNowDir item#getNextDir;
;;

let keyboard ~key ~x ~y =
    match key with
        | 27 -> exit 0
        | 113 -> exit 0
(*        | 97  -> monster#setNextDir 0 (*a*)
        | 119 -> monster#setNextDir 1 (*w*)
        | 100 -> monster#setNextDir 2 (*d*)
        | 115 -> monster#setNextDir 3 (*s*)
*)
        | _ -> ignore (Printf.printf "Key: %c %d pressed.\n%!" (char_of_int key) key)
;;

let special_key ~key ~x ~y =
    match key with
        | Glut.KEY_LEFT -> pacman#setNextDir 0
        | Glut.KEY_UP -> pacman#setNextDir 1
        | Glut.KEY_RIGHT -> pacman#setNextDir 2
        | Glut.KEY_DOWN -> pacman#setNextDir 3
        | _ -> print_endline("Special key pressed")
;;


let move_item item =
    if (item#getNextDir = 0) &&
        not (check_hit_bar_x (((float((item#getPoint)#getX) -. (float(item#getSpeed))) /. 100.) -. item#getRadius) 
                              (float((item#getPoint)#getY) /. 100.)) 
        then set_item_v (- item#getSpeed) 0 item
    else if (item#getNextDir = 1) &&
        not (check_hit_bar_y  (float((item#getPoint)#getX) /. 100.)
                             (((float((item#getPoint)#getY) +. (float(item#getSpeed))) /. 100.) +. item#getRadius)) 
        then set_item_v 0 item#getSpeed item
    else if (item#getNextDir = 2) &&
        not (check_hit_bar_x (((float((item#getPoint)#getX) +. (float(item#getSpeed))) /. 100.) +. item#getRadius) 
                              (float((item#getPoint)#getY) /. 100.)) 
        then set_item_v item#getSpeed 0 item
    else if (item#getNextDir = 3) &&
        not (check_hit_bar_y  (float((item#getPoint)#getX) /. 100.)
                             (((float((item#getPoint)#getY) -. (float(item#getSpeed))) /. 100.) -. item#getRadius)) 
        then set_item_v 0 (- item#getSpeed) item
    else if (item#getNextDir = -1)
        then set_item_v 0 0 item;
(*
    if (!px +. !vx < 1.) || (!px +. !vx > 29.) then vx := 0.;
    if (!py +. !vy < 1.) || (!py +. !vy > 29.) then vy := 0.;
*)
    if (item#getVelocity)#getX > 0 && 
       (check_hit_bar_x ((float((item#getPoint)#getX) +. float((item#getVelocity)#getX)) /. 100. +. item#getRadius) 
                        ((float((item#getPoint)#getY) +. float((item#getVelocity)#getY)) /. 100.) ) 
        then (item#getVelocity)#setX 0
    else if (item#getVelocity)#getX < 0 && 
       (check_hit_bar_x ((float((item#getPoint)#getX) +. float((item#getVelocity)#getX)) /. 100. -. item#getRadius) 
                        ((float((item#getPoint)#getY) +. float((item#getVelocity)#getY)) /. 100.) ) 
        then (item#getVelocity)#setX 0
    else if (item#getVelocity)#getY > 0 && 
       (check_hit_bar_y ((float((item#getPoint)#getX) +. float((item#getVelocity)#getX)) /. 100.) 
                        ((float((item#getPoint)#getY) +. float((item#getVelocity)#getY)) /. 100. +. item#getRadius) ) 
        then (item#getVelocity)#setY 0
    else if (item#getVelocity)#getY < 0 && 
       (check_hit_bar_y ((float((item#getPoint)#getX) +. float((item#getVelocity)#getX)) /. 100.) 
                        ((float((item#getPoint)#getY) +. float((item#getVelocity)#getY)) /. 100. -. item#getRadius) ) 
        then (item#getVelocity)#setY 0;
    if ((item#getVelocity)#getX = 0) && ((item#getVelocity)#getY = 0) then item#setNowDir (-1);
    (item#getPoint)#setX ((item#getPoint)#getX + (item#getVelocity)#getX);
    (item#getPoint)#setY ((item#getPoint)#getY + (item#getVelocity)#getY);
;;

    

let draw_item item = 
    GlDraw.color (item#getColor);
    GlDraw.begins `polygon;
    for i=0 to 359 do
        List.iter GlDraw.vertex2 (circle (2. *. pi *. float i /. 360.) (float((item#getPoint)#getX) /. 100.) (float((item#getPoint)#getY) /. 100.) (item#getRadius))
    done;
    GlDraw.ends ();
;;



let r0 = ref 0;;
let r1 = ref 0;;
let r2 = ref 0;;
let r3 = ref 0;;
let r_array = [|r0; r1; r2; r3|];;
let d0 = ref 0;;
let d1 = ref 0;;
let d2 = ref 0;;
let d3 = ref 0;;
let dis_now = ref 0;;
let d_array = [|d0; d1; d2; d3|];;
let get_weight_random_dir ran = 
    print_int(!(r_array.(0)));
    print_string(";");
    print_int(!(r_array.(1)));
    print_string(";");
    print_int(!(r_array.(2)));
    print_string(";");
    print_int(!(r_array.(3)));
    print_string(";;;;;;;");
    print_int(ran);
    print_endline("");
    if ran < !r0 then 0
    else if ran < (!r0) + (!r1) then 1
    else if ran < (!r0) + (!r1) + (!r2) then 2
    else 3
;;

let set_bar_dir item = 
    
    if  (check_hit_bar_x (((float((item#getPoint)#getX) -. (float(item#getSpeed))) /. 100.) -. item#getRadius) 
                              (float((item#getPoint)#getY) /. 100.)) 
        then r0 := 1;
    if  (check_hit_bar_y  (float((item#getPoint)#getX) /. 100.)
                             (((float((item#getPoint)#getY) +. (float(item#getSpeed))) /. 100.) +. item#getRadius)) 
        then r1 := 1;
    if  (check_hit_bar_x (((float((item#getPoint)#getX) +. (float(item#getSpeed))) /. 100.) +. item#getRadius) 
                              (float((item#getPoint)#getY) /. 100.)) 
        then r2 := 1;
    if  (check_hit_bar_y  (float((item#getPoint)#getX) /. 100.)
                             (((float((item#getPoint)#getY) -. (float(item#getSpeed))) /. 100.) -. item#getRadius)) 
        then r3 := 1;
;;

let calculate_dist item = 
    d0 := abs((item#getPoint#getX - 100) - pacman#getPoint#getX) +
          abs((item#getPoint#getY) - pacman#getPoint#getY);
    d1 := abs((item#getPoint#getX) - pacman#getPoint#getX) +
          abs((item#getPoint#getY + 100) - pacman#getPoint#getY);
    d2 := abs((item#getPoint#getX + 100) - pacman#getPoint#getX) +
          abs((item#getPoint#getY) - pacman#getPoint#getY);
    d3 := abs((item#getPoint#getX) - pacman#getPoint#getX) +
          abs((item#getPoint#getY - 100) - pacman#getPoint#getY);
    dis_now := abs((item#getPoint#getX) - pacman#getPoint#getX) +
               abs((item#getPoint#getY) - pacman#getPoint#getY);
;;

let lv2_ai item =  
    r0 := 25;
    r1 := 25;
    r2 := 25;
    r3 := 25;
    if item#getNowDir >=0 && item#getNowDir <=3 then
        r_array.(item#getNowDir) := 5;
    calculate_dist item;
    for i=0 to 3 do
        if (!(d_array.(i)) < !dis_now) then
            r_array.(i) := 100;
    done;
    set_bar_dir item;
    get_weight_random_dir (Random.int (!r0 + !r1 + !r2 + !r3))
;;
let lv1_ai item =
    r0 := 25;
    r1 := 25;
    r2 := 25;
    r3 := 25;
    set_bar_dir item;
    get_weight_random_dir (Random.int (!r0 + !r1 + !r2 + !r3))
;;
let set_item_next_dir item =
    item#setMoveCounter 13;
    item#setNextDir (lv2_ai item);
    print_int(item#getNextDir);
    print_endline("");
;;

let set_pacman player =
   pacman#setSpeed player#getSpeed;
   pacman#getPoint#setX player#getPoint#getX;
   pacman#getPoint#setY player#getPoint#getY;
   pacman#setColor player#getColor;

;;


let gene_gift mainMap x y = 
        let gift = new item in
        gift#setType 2;
        (gift#getPoint)#setX ((x*100)+50);
        (gift#getPoint)#setY ((y*100)+50);
        gift#setColor (0.3, 0.3, 0.);
        gift#setRadius 0.1;
        mainMap#addGift gift;;

let generate_g mainMap= 
for x=6 to 10 do
    for y=6 to 10 do
        if map_bar.(y).(x) = 0 then
            gene_gift mainMap x y;
    done;
done;;


let set_map mainMap =
   gmap#setPlayer mainMap#getPlayer;
   gmap#assgnMonsters mainMap#getMonsters;
   gmap#assgnGifts mainMap#getGifts;
   gmap#assgnBars mainMap#getBars;
   set_pacman mainMap#getPlayer;   
   generate_g gmap;
;;


let show_pacman mainMap =
  
  set_map mainMap;  
  ignore (Glut.init Sys.argv);
  Glut.initDisplayMode ~double_buffer:true ();
  Glut.initWindowSize ~w:512 ~h:512;
  ignore (Glut.createWindow ~title:"OpenGL Demo");
  GlClear.color (0.0, 0.0, 0.0);
  GlDraw.shade_model `smooth;
  let render () =
    GlMat.mode `projection;
    GlMat.load_identity ();
    GluMat.ortho2d ~x:(0., map_width) ~y:(0., map_height);
    GlMat.mode `modelview;
    GlMat.load_identity ();
    GlClear.clear [ `color ];
   

    move_item pacman;

    let deal_gift g p =
        total_score := (!total_score + g#getScore);
        print_string("Score: ");
        print_int(!total_score);
        print_endline("");
        g#setLevel (- 1);
    in let set_n_draw_gift g = 
        draw_item g;
    in let check_gift g p = 
        if (abs (p#getPoint#getX - g#getPoint#getX) + 
            abs (p#getPoint#getY - g#getPoint#getY) )
            < (truncate((p#getRadius +. g#getRadius) *. 100.)) 
            then 
                deal_gift g p
        else
            set_n_draw_gift g;
    in let rec check_items glist player = 
        match glist with
        []->[]
        | g1::tl -> 
            check_gift g1 player;
            if ((g1#getLevel) != -1) then
                g1::(check_items tl player)
            else
                check_items tl player
    in 
    gmap#setGifts (check_items gmap#getGifts pacman);

    draw_item pacman;

   let move_monster monster = 
        if (check_item_change_dir monster) then
            set_item_next_dir monster;
        move_item monster;
        monster#setMoveCounter (monster#getMoveCounter -1);
    in let rec check_items mlist = 
        match mlist with
        []->()
        | m1::tl -> 
            move_monster m1;	  
            draw_item m1;
            check_items tl;
    in check_items gmap#getMonsters;


    GlDraw.color colorb;

    let draw_box x y width = 
        GlDraw.begins `line_loop;
        List.iter GlDraw.vertex2 [x *. width, y *. width; 
                                  (x +. width ) *. width, y *. width; 
                                  (x +. width ) *. width, (y +.  width ) *. width; 
                                  x *. width, (y +.  width ) *. width];
        GlDraw.ends ();
    in
    let rec print_map_raw r x y = 
        if float(x) < map_width then
            if r.(x) = 1 then
                draw_box (float(x)) (float(y)) width;
        if float(x) < map_width then
            print_map_raw r (x+1) y;
    in let rec print_map_bar m y = 
        if float(y) < map_height then
            print_map_raw m.(y) 0 y;
        if float(y) < map_height then
            print_map_bar m (y+1);
    in print_map_bar map_bar 0;


    Gl.flush ();
    Glut.swapBuffers () in
  Glut.displayFunc ~cb:render;
  Glut.idleFunc ~cb:(Some Glut.postRedisplay);
  Glut.keyboardFunc ~cb:(keyboard);
  Glut.specialFunc ~cb:(special_key);
  Glut.mainLoop ()
;;

